#include "omip.h"
#include "omip_log.h"
#include "osal.h"
#include "securec.h"
#include "los_task.h"

typedef enum {
    STATE_BLE8258_RECV_START,
    STATE_BLE8258_RECV_DATA_LEN,
    STATE_BLE8258_RECV_FIXED_HEAD,
    STATE_BLE8258_RECV_DATA,
}ble_recv_state;

/*recv from ble uart example*/
/*
**| start  |  len   |  fixed  |  payload  |
**| 1 byte | 1 byte | 5 bytes | 247 bytes |
*/
/*example: FF 10 A0 07 52 15 00 68 65 6C 6C 6F 20 68 6C 69 6E 6B */
#define BLE8258_MTU  250
#define BLE8258_PAYLOAD_MAX             (BLE8258_MTU - 3)   //mtu - 3 bytes of crc
#define BLE8258_RECV_FRAME_START        0xFF
#define BLE8258_RECV_FRAME_HEAD_LEN     2  /* start + len */
#define BLE8258_RECV_FIXED_DATA_LEN     5
#define BLE8258_RECV_FRAME_MAX_LEN      (BLE8258_RECV_FRAME_HEAD_LEN + BLE8258_RECV_FIXED_DATA_LEN + BLE8258_PAYLOAD_MAX)
static unsigned char g_ble_8258_recv_fixed[] = {0xA0,0x07,0x52,0x15,0x00};

/*send to ble uart example*/
/*len must modified when send data */
/*
**|  start  |   len   |  fixed  |  payload  |
**| 2 bytes | 2 bytes | 2 bytes | 247 bytes |
*/
/*example: 1C FF 03 00 11 00 6B*/
static unsigned char ble_8258_send_header[] = {0x1C,0xFF,0x10,00,0x11,0x00};
#define BLE8258_SEND_FRAME_HEAD_LEN     2  /* start */
#define BLE8258_SEND_FIXED_DATA_LEN     2  /* fixed */
#define BLE8258_SEND_FRAME_MAX_LEN      (BLE8258_SEND_FIXED_DATA_LEN + BLE8258_PAYLOAD_MAX)  /* fixed + payload */

static unsigned char ble_send_frame_buf[OMIP_FRM_MAX_LEN * 2];

extern void omip_receive_byte(unsigned char data, int io_id);
extern void omip_send_byte(unsigned char data, unsigned int io_id);

int get_send_payload_max(void)
{
    return BLE8258_PAYLOAD_MAX;
}

int send_ble8258_header(unsigned short len, int io_id)
{
    ble_8258_send_header[BLE8258_SEND_FRAME_HEAD_LEN] = (unsigned char)len;
    omip_printf(OMIP_DEBUG,"send ble frame,len:%d,io_id:%d\n", len,io_id);
    for(int i = 0; i < sizeof(ble_8258_send_header); i++)
    {
        omip_send_byte(ble_8258_send_header[i], io_id);
        // omip_printf(OMIP_DEBUG,"%x\t", ble_8258_send_header[i]);
    }
    return 0;
}

int send_ble8258_data(unsigned char *data, unsigned short len, int io_id)
{
    for(int i = 0; i < len; i++)
    {
        omip_send_byte(data[i], io_id);
        // omip_printf(OMIP_DEBUG,"%x\t", data[i]);
    }
    // omip_printf(OMIP_DEBUG,"\n");
    return 0;
}
extern unsigned int recv_req_time;
extern unsigned int send_rsp_time;

void send_ble8258_frame(unsigned char *data, unsigned short len, int io_id)
{
    unsigned short left_len;
    unsigned char *pd;
    int i,j;

    ble_send_frame_buf[0] = data[0];//start
    j = 1;
    for(i = 1; i < len - 1; i++)
    {
        switch (data[i]) {
            case OMIP_START:
                ble_send_frame_buf[j] = OMIP_ESC;
                j++;
                ble_send_frame_buf[j] = OMIP_ESC_START;
                j++;
                break;
            case OMIP_END:
                ble_send_frame_buf[j] = OMIP_ESC;
                j++;
                ble_send_frame_buf[j] = OMIP_ESC_END;
                j++;
                break;
            case OMIP_ESC:
                ble_send_frame_buf[j] = OMIP_ESC;
                j++;
                ble_send_frame_buf[j] = OMIP_ESC_ESC;
                j++;
                break;
            default:
                ble_send_frame_buf[j] = data[i];
                j++;
                break;
        }
    }
    ble_send_frame_buf[j] = data[len-1];
    j++;

    left_len = j;

    //frame = header+data
    pd = ble_send_frame_buf;
    
    while (left_len > BLE8258_PAYLOAD_MAX) {
        LOS_TaskLock(); //attention
        send_ble8258_header(BLE8258_PAYLOAD_MAX + BLE8258_SEND_FIXED_DATA_LEN, io_id);
        send_ble8258_data(pd,BLE8258_PAYLOAD_MAX, io_id);
        LOS_TaskUnlock();
        left_len -= BLE8258_PAYLOAD_MAX;
        pd += BLE8258_PAYLOAD_MAX;
        osal_task_sleep(20);// ble message chip
    }
    //last frame
    LOS_TaskLock(); //attention
    send_ble8258_header(left_len + BLE8258_SEND_FIXED_DATA_LEN, io_id);
    send_ble8258_data(pd, left_len, io_id);
    LOS_TaskUnlock();
    osal_task_sleep(20); // frame and frame

    send_rsp_time = osal_sys_time();
    unsigned int consume_time = send_rsp_time - recv_req_time;
    omip_printf(OMIP_DEBUG, "consume_time:%d,start:%d,end:%d\n",consume_time,recv_req_time,send_rsp_time);
}

#define BLE_SKB_INFO_NUM 8*10
typedef struct {
    int start; // ble frame head
    int len;  // ble frame len, the second btye in a ble frame
}ble_skb_info;

typedef struct {
    ble_skb_info skb[BLE_SKB_INFO_NUM]; // MTU num
    int write_index; // MTU index
    int read_index;
    osal_semp_t  recv_ble8258_sem;
}ble_skb_info_mn;

#define BLE_RECV_BUF_SIZE 1024
typedef struct {
    unsigned char low_recv_buf[BLE_RECV_BUF_SIZE];  // ble recv buf, store ble frame
    int low_recv_index;  // ble recv index, if left buf size < a MTU(250), start from 0
}ble_recv_buf_nm;

static ble_skb_info_mn skb_nm;
static ble_recv_buf_nm recv_nm;

ble_recv_state g_recv_state_machine = STATE_BLE8258_RECV_START;

unsigned char g_recv_fixed_count = 0;
unsigned char g_recv_data_count = 0;

static int ble_recv_bytes = 0;

void recve_ble8258_byte(unsigned char data, int io_id)
{
    ble_recv_bytes++;
    switch(g_recv_state_machine)
    {
        case STATE_BLE8258_RECV_START:
            if(data == BLE8258_RECV_FRAME_START)
            {
                g_recv_state_machine = STATE_BLE8258_RECV_DATA_LEN;
                if ((BLE_RECV_BUF_SIZE - recv_nm.low_recv_index) < BLE8258_PAYLOAD_MAX)
                {
                    recv_nm.low_recv_index = 0;
                }
                skb_nm.skb[skb_nm.write_index].start = recv_nm.low_recv_index;
            }
            break;
        case STATE_BLE8258_RECV_DATA_LEN:
            if(data > (BLE8258_RECV_FIXED_DATA_LEN + BLE8258_PAYLOAD_MAX) || data < (BLE8258_RECV_FIXED_DATA_LEN + 1))//at least one byte(valid data)
            {
                g_recv_state_machine = STATE_BLE8258_RECV_START;
                break;
            }
            g_recv_fixed_count = 0;
            g_recv_data_count = 0;
            skb_nm.skb[skb_nm.write_index].len = data;
            g_recv_state_machine = STATE_BLE8258_RECV_FIXED_HEAD;
            break;
        case STATE_BLE8258_RECV_FIXED_HEAD:
            if(data != g_ble_8258_recv_fixed[g_recv_fixed_count])
            {
                if(data == BLE8258_RECV_FRAME_START)
                {
                     g_recv_state_machine = STATE_BLE8258_RECV_DATA_LEN;
                }
                else
                {
                     g_recv_state_machine = STATE_BLE8258_RECV_START;
                }
                break;
            }
            g_recv_fixed_count++;
            g_recv_data_count++;
            if(g_recv_fixed_count == BLE8258_RECV_FIXED_DATA_LEN)
            {
                g_recv_state_machine = STATE_BLE8258_RECV_DATA;
            }
            break;
        case STATE_BLE8258_RECV_DATA:
            g_recv_data_count++;
            recv_nm.low_recv_buf[recv_nm.low_recv_index++] = data;
            if(g_recv_data_count == skb_nm.skb[skb_nm.write_index].len)
            {
                g_recv_state_machine = STATE_BLE8258_RECV_START;
                if(++skb_nm.write_index >= BLE_SKB_INFO_NUM)
                    skb_nm.write_index = 0;
                osal_semp_post(skb_nm.recv_ble8258_sem);
            }
            break;

         default:
            break;
    }

}

void ble_recv_task_entry(void *arg)
{
    int pos;
    while(1)
    {
        osal_semp_pend(skb_nm.recv_ble8258_sem, 1000);

        if(skb_nm.write_index == skb_nm.read_index)
        {
            continue;
        }
        omip_printf(OMIP_DEBUG,"read skb index:%d, start:%d, len:%d\n",skb_nm.read_index, skb_nm.skb[skb_nm.read_index].start,
            skb_nm.skb[skb_nm.read_index].len);
        pos = skb_nm.skb[skb_nm.read_index].start;
        for(int i = 0; i < skb_nm.skb[skb_nm.read_index].len - BLE8258_RECV_FIXED_DATA_LEN; i++)
        {
            omip_printf(OMIP_DEBUG,"data:%x, pos:%d\n", recv_nm.low_recv_buf[pos], pos);
            omip_receive_byte(recv_nm.low_recv_buf[pos], 1);//use uart2,(index=1)
            pos++;
        }

        if(++skb_nm.read_index >= BLE_SKB_INFO_NUM)
            skb_nm.read_index = 0;
    }
}

void ble8258_support_start(void)
{
    int ret = 0;

    memset_s(&skb_nm, sizeof(skb_nm), 0, sizeof(skb_nm));
    memset_s(&recv_nm, sizeof(recv_nm), 0, sizeof(recv_nm));
    ret = osal_semp_create(&skb_nm.recv_ble8258_sem, 256, 0);
    if (ret != OSAL_OK)
    {
        omip_printf(OMIP_ERROR, "create recv_ble8258_sem failed\n");
    }
    void *pid = osal_task_create("ble_recv_task",(int (*)(void *))ble_recv_task_entry, NULL, 1024*4, NULL, 23);
    if(NULL == pid)
        omip_printf(OMIP_ERROR, "create ble_recv_task failed\n");
}


